package pl.edu.agh.student.nn.core;

import pl.edu.agh.student.nn.core.bp.BPLayer;
import pl.edu.agh.student.nn.core.cp.GrossbergLayer;
import pl.edu.agh.student.nn.core.kohonen.KohonenLayer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Reprezentuje pojedyncza warstwe sieci neuronowej. Zawiera neurony tej warstwy.
 */
public class Layer {
    protected String name;
    protected List<Neuron> neurons = new ArrayList<Neuron>();
    protected LayerType type;
    protected Map<LayerParameter, List<Double>> doubleParameters = new HashMap<LayerParameter, List<Double>>();
    protected Map<LayerParameter, List<Integer>> intParameters = new HashMap<LayerParameter, List<Integer>>();
    private int size;

    public Layer(int size, LayerType type) {
        this.size = size;
        this.type = type;
    }

    /**
     * Oblicza odpowiedzi wszystkich nauronow z warstwy sieci.
     */
    public void calculate() {
        for (Neuron n : neurons) {
            n.calculate();
        }
    }

    public void learn(int currentIterations, TrainingSet trainingSet) {

    }

    public double getDoubleParam(LayerParameter param, int currentIteration) {
        int i = 0;
        for (Integer iterations : intParameters.get(LayerParameter.ITERATIONS)) {
            if (currentIteration < iterations) {
                break;
            }
            i++;
        }
        return doubleParameters.get(param).get(i);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean addNeuron(Neuron neuron) {
        return neurons.add(neuron);
    }

    public boolean removeNeuron(Neuron neuron) {
        return neurons.remove(neuron);
    }

    public List<Neuron> getNeurons() {
        return neurons;
    }

    public void setNeurons(List<Neuron> neurons) {
        this.neurons = neurons;
    }

    /**
     * Wypisanie na standardowe wyjscie struktury warstwy
     */
    public String display() {
        StringBuffer sb = new StringBuffer();
        sb.append(getType()).append("|").append(getName()).append("\n");
        for (LayerParameter p : intParameters.keySet()) {
            sb.append(p.getName() + " " + intParameters.get(p) + "\n");
        }
        for (LayerParameter p : doubleParameters.keySet()) {
            sb.append(p.getName() + " " + doubleParameters.get(p) + "\n");
        }
        for (int i = 0; i < neurons.size(); i++) {
            sb.append(neurons.get(i).display()).append("\n");
        }
        return sb.toString();
    }

    public LayerType getType() {
        return type;
    }

    public void setType(LayerType type) {
        this.type = type;
    }

    public Map<LayerParameter, List<Double>> getDoubleParameters() {
        return doubleParameters;
    }

    public void setDoubleParameters(Map<LayerParameter, List<Double>> doubleParameters) {
        this.doubleParameters = doubleParameters;
    }

    public Map<LayerParameter, List<Integer>> getIntParameters() {
        return intParameters;
    }

    public void setIntParameters(Map<LayerParameter, List<Integer>> intParameters) {
        this.intParameters = intParameters;
    }

    public static Layer createLayer(String serializedForm) {
        String layerDetails[] = serializedForm.split("\\|");
        switch (LayerType.valueOf(layerDetails[0])) {
            case S:
                return new Layer(Integer.parseInt(layerDetails[1]), LayerType.S);
            case KOH:
                String size[] = layerDetails[1].split("x");
                return new KohonenLayer(Integer.parseInt(size[0]), Integer.parseInt(size[1]));
            case WH:
                return new GrossbergLayer(Integer.parseInt(layerDetails[1]), LayerType.WH);
            case DELTA:
                return new GrossbergLayer(Integer.parseInt(layerDetails[1]), LayerType.DELTA);
            case BP:
                return new BPLayer(Integer.parseInt(layerDetails[1]), LayerType.BP);
        }
        return null;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}
